package com.highper.gateway.filter;

import cn.dev33.satoken.id.SaIdUtil;
import com.alibaba.fastjson.JSONObject;
import com.highper.common.constant.ExceptionMessageConstant;
import com.highper.common.constant.NumberConstant;
import com.highper.common.constant.ResponseCodeConstant;
import com.highper.common.entity.response.HighperResponse;
import com.highper.common.mapper.GatewayListMapper;
import com.highper.common.sehcma.ContextHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;

import static com.highper.common.constant.SchemaConstant.HIGHPER_GATEWAY;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*;

/**
 * @Author ZhangGJ
 * @Date 2022/01/24 15:55
 */
@Slf4j
@Order(0)
@Component
public class GatewayRequestFilter implements GlobalFilter {

    private final GatewayListMapper gatewayListMapper;

    @Autowired
    public GatewayRequestFilter(GatewayListMapper gatewayListMapper) {
        this.gatewayListMapper = gatewayListMapper;
    }

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        Mono<Void> checkForbidUriResult = checkForbidUri(request, response);
        if (checkForbidUriResult != null) {
            return checkForbidUriResult;
        }
        printLog(exchange);
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        String routeId = null;
        if (route != null && !StringUtils.isEmpty(route.getId())) {
            routeId = route.getId();
        }
        ServerHttpRequest newRequest =
                request.mutate().header(SaIdUtil.ID_TOKEN, SaIdUtil.getToken())
                        .header("route", routeId).build();
        ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
        return chain.filter(newExchange);
    }

    private Mono<Void> checkForbidUri(ServerHttpRequest request, ServerHttpResponse response) {
        String uri = request.getPath().toString();
        ContextHandler.setSchema(HIGHPER_GATEWAY);
        String[] blacks = gatewayListMapper.selectGatewayList(NumberConstant.ZERO);
        boolean shouldForward = true;
        if (blacks != null && ArrayUtils.isNotEmpty(blacks)) {
            for (String u : blacks) {
                if (pathMatcher.match(u, uri)) {
                    shouldForward = false;
                }
            }
        }
        if (!shouldForward) {
            return makeResponse(response,
                    HighperResponse.failure(ResponseCodeConstant.GATEWAY_ERROR,
                            ExceptionMessageConstant.URI_FORBIDDEN));
        }
        return null;
    }

    private Mono<Void> makeResponse(ServerHttpResponse response, HighperResponse highperResponse) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        DataBuffer dataBuffer =
                response.bufferFactory().wrap(JSONObject.toJSONString(highperResponse).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }

    private void printLog(ServerWebExchange exchange) {
        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        LinkedHashSet<URI> uris = exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        URI originUri = null;
        if (uris != null) {
            originUri = uris.stream().findFirst().orElse(null);
        }
        if (url != null && route != null && originUri != null) {
            log.info("转发请求：{}://{}{} --> 目标服务：{}，目标地址：{}://{}{}，转发时间：{}", originUri.getScheme(),
                    originUri.getAuthority(), originUri.getPath(), route.getId(), url.getScheme(),
                    url.getAuthority(), url.getPath(), LocalDateTime.now());
        }
    }

}
